#include "log.h"

#define debug(fmt, arg...) fprintf(stderr, fmt "\n", ##arg)
#define PARSE_CHAR(c) ((c) < 0x21 ? '.' : (c) > 126 ? '.' : (c))

log_t Log;

static int get_timestrap(char* tmstrap)
{
    struct tm *p, tm_p;
    time_t timep;

    time(&timep);
    localtime_r(&timep, &tm_p);
    p = &tm_p;
    return sprintf(tmstrap, "[%d/%d/%d %d:%d:%d]",
        1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
}

static int str2level(char* str)
{
    if (!strcasecmp(str, "trace"))
        return LOG_TRACE;
    if (!strcasecmp(str, "debug"))
        return LOG_DEBUG;
    if (!strcasecmp(str, "info"))
        return LOG_INFO;
    if (!strcasecmp(str, "notice"))
        return LOG_NOTICE;
    if (!strcasecmp(str, "warn"))
        return LOG_WARN;
    if (!strcasecmp(str, "error"))
        return LOG_ERROR;
    if (!strcasecmp(str, "fatal"))
        return LOG_FATAL;

    return LOG_FATAL;
}
static void check_log_size(log_t *log){
    struct stat st;

    if (log->fp != stdout) {
        stat(log->file_name, &st);
        if (st.st_size > log->log_size) {
            freopen(log->file_name, "w", log->fp);
        }
    }
}
int log_write(log_t* log, int level, char* fmt, ...)
{
    int len = 0;
    char sprint_buf[128] = { 0 };
    va_list args;
    
    check_log_size(log);

    if (level <= log->level) {
        if(level != LOG_OUTPUT)
        {
            len = get_timestrap(sprint_buf);
        }
        va_start(args, fmt);
        vsprintf(sprint_buf + len, fmt, args);
        va_end(args);
        if (0 > fputs(sprint_buf, log->fp)) {
            return 0;
        }
        if (log->level >= LOG_DEBUG)
            fflush(log->fp);
    }

    return 1;
}

int log_buffer(log_t* log, int level, unsigned char* buf, int buf_len)
{
    int i = 0;
    
    check_log_size(log);

    if (level <= log->level) {
        for (i = 0; i < buf_len; i++) {
            fprintf(log->fp, "0x%2x(%c) ", buf[i], PARSE_CHAR(buf[i]));
            fflush(log->fp);
            if ((i + 1) % 16 == 0)
                fprintf(log->fp, "\n");
        }
        if (i % 16)
            fprintf(log->fp, "\n");
        fprintf(log->fp, "^^^^^^^^^^^^^buf len:%d^^^^^^^^^^^^^\n", buf_len);
    }
    return 1;
}
static int log_opt(log_t *log, int argc, char** argv)
{
    char log_level[32] = { 0 };
    OPT_T opt[] = {
        { "--log-level", log_level, OPT_STRING, "set log level." },
        { "--log-size", &log->log_size, OPT_INT, "set log file max size." },
        { "--log-output", log->file_name, OPT_STRING, "set output file." },
        { "--log-buf", &log->buf_size, OPT_INT, "set log buffer size." },
    };
    opt_parse(opt, sizeof(opt), argc, argv);
    log->level = str2level(log_level);
    if (!strcmp("stdout", log->file_name)) //output to stdout
    {
       return 1;
    }

    log->fp = fopen(log->file_name, "w");
    if(log->buf_size){
        log->buffer = malloc(log->buf_size);
        setbuf(log->fp, log->buffer);
    }
    return 1;
}
int log_set_level(log_t *log, char *level){
    log->level = str2level(level);
    return 1;
}
static int log_default(log_t *log)
{
    log->level = LOG_OUTPUT;
    log->fp = stdout;
    strcpy(log->file_name, "stdout");
    log->log_size = 1024 * 10;
    log->buf_size = 0;
    return 1;
}
int log_initv(int argc, char** argv)
{
    log_default(&Log);
    log_opt(&Log, argc, argv);
    return 1;
}
int log_init(char* output, int log_size, char* level)
{
    log_default(&Log);

    if (!strcmp(output, "stdout") || !strcmp(output, "stderr")) {
        Log.fp = stdout;
        strcpy(Log.file_name, "stdout");
    } else {
        Log.fp = fopen(output, "w");
        strcpy(Log.file_name, output);
    }
    Log.log_size = log_size;
    Log.level = str2level(level);
    return 1;
}